package com.rxkj.syslog;

import com.rxkj.entity.sys.SysOprateLog;
import com.rxkj.entity.sys.UserInfo;
import com.rxkj.service.SysOPLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志aop
 * by alex
 * 2022-4-19
 */
@Aspect
@Component
public class SysOPLogAspect {

    @Autowired
    private SysOPLogService sysOPLogService;

    /**
     * 定义切点
     * @Pointcut 注解的所在路径
     */
    @Pointcut("@annotation(com.rxkj.syslog.SysOPLog)")
    public void logPointCut() {
    }

    @AfterReturning(value = "logPointCut() && @annotation(logger)", returning = "ret")
    public void saveOperationLog(JoinPoint joinPoint, SysOPLog logger, Object ret) throws IOException {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Map<String, Object> fn = getFieldsName(joinPoint);
        HttpServletRequest req = (HttpServletRequest) fn.get("request");
        //创建一个操作日志实体类存放此次操作信息
        SysOprateLog opl = new SysOprateLog();
        //获取uri
        String requestURI = request.getRequestURI();
        //操作的url
        opl.setRequrl(requestURI);

        if ("/sys/login".equals(requestURI)){
            if ((Integer) ((Map<String, Object>)ret).get("code") == 200){
                UserInfo ui = (UserInfo) ((Map<String, Object>)ret).get("user");
                //操作人
                opl.setUname(ui.getUserName());
                opl.setUid(ui.getId());
            }else {
                return;
            }
        }else {
            Integer id = (Integer) req.getSession().getAttribute("id");
            String userName = (String) req.getSession().getAttribute("userName");
            opl.setUname(userName);
            opl.setUid(id);
        }

        //操作内容描述
        opl.setOpcontent(logger.content());
        //操作类型
        opl.setLogtype(logger.logType());
        //操作时间
        opl.setOptime(new Date());
        //获取真是ip
        String realip = getRemortIP(req);
        opl.setOpip(realip);
        //若有返回结果,封装返回信息
        if (null != ret) {
            //响应内容
            opl.setResplog(ret.toString());
        }
        //将操作日志写入数据库
        sysOPLogService.savesyslog(opl);

    }

    //获取注解上 userName的动态传参
    private String getUserName(JoinPoint joinPoint, SysOPLog logger){
        String userNameSpel = logger.userName();
        Object[] args = joinPoint.getArgs();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer localVariableTable = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = localVariableTable.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        String userName = null;
        // 使用变量方式传入业务动态数据
        if(userNameSpel.matches("^#.*.$")) {
            userName = parser.parseExpression(userNameSpel).getValue(context, String.class);
        }
        return userName;
    }

    private Integer getId(JoinPoint joinPoint, SysOPLog logger){
        String idSpel = logger.id();
        Object[] args = joinPoint.getArgs();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer localVariableTable = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = localVariableTable.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        Integer id = null;
        // 使用变量方式传入业务动态数据
        if(idSpel.matches("^#.*.$")) {
            id = parser.parseExpression(idSpel).getValue(context, Integer.class);
        }
        return id;
    }

    private Map<String, Object> getFieldsName(JoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //方法所属类的类名
        String ClassName =  methodSignature.getDeclaringTypeName();
        //获取当前切点方法对象
        Method method = methodSignature.getMethod();
        //打印方法名
        System.out.println(method.getName());
        //方法参数的类型
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> clas : parameterTypes) {
            String parameterName = clas.getName();
            //System.out.println("参数类型:" + parameterName);

        }
        Map<String, Object> map = new HashMap<String, Object>();
        //参数名称
        String[] names=((MethodSignature) joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] objects=joinPoint.getArgs();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], objects[i]);
        }
        return map;
    }

    //获取真实IP一
    public String getRemortIP(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();
        return ipAddress;
    }

}
